package com.gitee.wsl.io.channel.write.ext

import com.gitee.wsl.annotation.InternalAPI
import com.gitee.wsl.io.channel.read.ByteReadChannel
import com.gitee.wsl.io.channel.write.ByteWriteChannel
import com.gitee.wsl.io.channel.write.flushIfNeeded
import com.gitee.wsl.io.size
import com.gitee.wsl.io.writeText
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.intrinsics.startCoroutineCancellable
import kotlinx.io.Buffer
import kotlinx.io.InternalIoApi
import kotlinx.io.Source
import kotlinx.io.UnsafeIoApi
import kotlinx.io.unsafe.UnsafeBufferOperations
import kotlin.coroutines.Continuation
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext


@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeByte(value: Byte) {
    writeBuffer.writeByte(value)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeShort(value: Short) {
    writeBuffer.writeShort(value)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeInt(value: Int) {
    writeBuffer.writeInt(value)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeLong(value: Long) {
    writeBuffer.writeLong(value)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeByteArray(array: ByteArray) {
    writeBuffer.write(array)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeSource(source: Source) {
    writeBuffer.transferFrom(source)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeString(value: String) {
    writeBuffer.writeText(value)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size) {
    writeBuffer.write(value, startIndex, endIndex)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeBuffer(value: Source) {
    writeBuffer.transferFrom(value)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writeStringUtf8(value: String) {
    writeBuffer.writeText(value)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writePacket(copy: Buffer) {
    writeBuffer.transferFrom(copy)
    flushIfNeeded()
}

@OptIn(InternalAPI::class)
 suspend fun ByteWriteChannel.writePacket(copy: Source) {
    writeBuffer.transferFrom(copy)
    flushIfNeeded()
}

 fun ByteWriteChannel.close(cause: Throwable?) {
    if (cause == null) {
        ::flushAndClose.fireAndForget()
    } else {
        cancel(cause)
    }
}

/**
 * Await for [desiredSpace] will be available for write and invoke [block] function providing [Memory] instance and
 * the corresponding range suitable for wiring in the memory. The block function should return number of bytes were
 * written, possibly 0.
 *
 * Similar to [ByteReadChannel.read], this function may invoke block function with lesser memory range when the
 * specified [desiredSpace] is bigger that the buffer's capacity
 * or when it is impossible to represent all [desiredSpace] bytes as a single memory range
 * due to internal implementation reasons.
 */
@OptIn(UnsafeIoApi::class, InternalAPI::class, InternalIoApi::class)
 suspend fun ByteWriteChannel.write(
    desiredSpace: Int = 1,
    block: (ByteArray, Int, Int) -> Int
): Int {
    val before = writeBuffer.size
    UnsafeBufferOperations.writeToTail(writeBuffer.buffer, desiredSpace, block)
    val after = writeBuffer.size
    val written = after - before
    flushIfNeeded()
    return written
}

suspend fun ByteWriteChannel.awaitFreeSpace() {
    flush()
}

@OptIn(InternalCoroutinesApi::class)
internal fun <R> (suspend () -> R).fireAndForget() {
    this.startCoroutineCancellable(NO_CALLBACK)
}

private val NO_CALLBACK = object : Continuation<Any?> {
    override val context: CoroutineContext = EmptyCoroutineContext

    override fun resumeWith(result: Result<Any?>) = Unit
}